
#include "TimeSpan.h"

#include <System/Math.h>
#include <System/Int32.h>

using namespace Riccsson::System;

const long long TimeSpan::TicksPerDay = 864000000000;
const long long TimeSpan::TicksPerHour = 36000000000;
const long long TimeSpan::TicksPerMillisecond = 10000;
const long long TimeSpan::TicksPerMinute = 600000000;
const long long TimeSpan::TicksPerSecond = 10000000;

readonly TimeSpan TimeSpan::MaxValue = TimeSpan(99);
readonly TimeSpan TimeSpan::MinValue = TimeSpan(-99);
readonly TimeSpan TimeSpan::Zero = TimeSpan(0);


//----------------------------------------------------------------------------
TimeSpan::TimeSpan()
	: PROP3GET_INIT(TimeSpan, Days)
	, PROP3GET_INIT(TimeSpan, Hours)
	, PROP3GET_INIT(TimeSpan, Milliseconds)
	, PROP3GET_INIT(TimeSpan, Minutes)
	, PROP3GET_INIT(TimeSpan, Seconds)
	, PROP3GET_INIT(TimeSpan, Ticks)
	, PROP3GET_INIT(TimeSpan, TotalDays)
	, PROP3GET_INIT(TimeSpan, TotalHours)
	, PROP3GET_INIT(TimeSpan, TotalMilliseconds)
	, PROP3GET_INIT(TimeSpan, TotalMinutes)
	, PROP3GET_INIT(TimeSpan, TotalSeconds)
{
	_ticks = 0;
}

//----------------------------------------------------------------------------
bool TimeSpan::CalculateTicks (int days, int hours, int minutes, int seconds, int milliseconds, bool throwExc, long long out result)
{
    int hrssec = (hours * 3600);
    int minsec = (minutes * 60);
    long long t = ((long long)(hrssec + minsec + seconds) * 1000L + (long long)milliseconds);
    t *= 10000;

    result = 0;

    bool overflow = false;
    if (days > 0)
	{
        long long td = TicksPerDay * days;
        if (t < 0)
		{
            long long ticks = t;
            t += td;
            overflow = (ticks > t);
        }
        else
		{
            t += td;
            overflow = (t < 0);
        }
    }
    else if (days < 0)
	{
        long long td = TicksPerDay * days;
        if (t <= 0)
		{
            t += td;
            overflow = (t > 0);
        }
        else
		{
            long long ticks = t;
            t += td;
            overflow = (t > ticks);
        }
    }

    if (overflow)
	{
        if (throwExc)
			throw ArgumentOutOfRangeException("The timespan is too big or too small.");
        return false;
    }

    result = t;
    return true;
}

//----------------------------------------------------------------------------
TimeSpan TimeSpan::From(double value, long long tickMultiplicator) 
{
    //if (Double::IsNaN (value))
    //        throw new ArgumentException("Value cannot be NaN.", "value");
	//
    //if (Double::IsNegativeInfinity (value) || Double::IsPositiveInfinity (value) || (value < MinValue.Ticks) || (value > MaxValue.Ticks))
    //        throw new OverflowException("Outside range [MinValue,MaxValue]");

    try
	{
		value = (value * (tickMultiplicator / TicksPerMillisecond));

        long long val = (long long) Math::Round(value);
        return TimeSpan (val * TicksPerMillisecond);
    }
    catch (const OverflowException&)
	{
		throw new OverflowException("Resulting timespan is too big.");
    }
}

TimeSpan::TimeSpan(long long ticks)
	: PROP3GET_INIT(TimeSpan, Days)
	, PROP3GET_INIT(TimeSpan, Hours)
	, PROP3GET_INIT(TimeSpan, Milliseconds)
	, PROP3GET_INIT(TimeSpan, Minutes)
	, PROP3GET_INIT(TimeSpan, Seconds)
	, PROP3GET_INIT(TimeSpan, Ticks)
	, PROP3GET_INIT(TimeSpan, TotalDays)
	, PROP3GET_INIT(TimeSpan, TotalHours)
	, PROP3GET_INIT(TimeSpan, TotalMilliseconds)
	, PROP3GET_INIT(TimeSpan, TotalMinutes)
	, PROP3GET_INIT(TimeSpan, TotalSeconds)
{
	_ticks = ticks;
}
TimeSpan::TimeSpan(int hours, int minutes, int seconds)
	: PROP3GET_INIT(TimeSpan, Days)
	, PROP3GET_INIT(TimeSpan, Hours)
	, PROP3GET_INIT(TimeSpan, Milliseconds)
	, PROP3GET_INIT(TimeSpan, Minutes)
	, PROP3GET_INIT(TimeSpan, Seconds)
	, PROP3GET_INIT(TimeSpan, Ticks)
	, PROP3GET_INIT(TimeSpan, TotalDays)
	, PROP3GET_INIT(TimeSpan, TotalHours)
	, PROP3GET_INIT(TimeSpan, TotalMilliseconds)
	, PROP3GET_INIT(TimeSpan, TotalMinutes)
	, PROP3GET_INIT(TimeSpan, TotalSeconds)
{
	CalculateTicks(0, hours, minutes, seconds, 0, true, _ticks);
}
TimeSpan::TimeSpan(int days, int hours, int minutes, int seconds)
	: PROP3GET_INIT(TimeSpan, Days)
	, PROP3GET_INIT(TimeSpan, Hours)
	, PROP3GET_INIT(TimeSpan, Milliseconds)
	, PROP3GET_INIT(TimeSpan, Minutes)
	, PROP3GET_INIT(TimeSpan, Seconds)
	, PROP3GET_INIT(TimeSpan, Ticks)
	, PROP3GET_INIT(TimeSpan, TotalDays)
	, PROP3GET_INIT(TimeSpan, TotalHours)
	, PROP3GET_INIT(TimeSpan, TotalMilliseconds)
	, PROP3GET_INIT(TimeSpan, TotalMinutes)
	, PROP3GET_INIT(TimeSpan, TotalSeconds)
{
	CalculateTicks(days, hours, minutes, seconds, 0, true, _ticks);
}
TimeSpan::TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds)
	: PROP3GET_INIT(TimeSpan, Days)
	, PROP3GET_INIT(TimeSpan, Hours)
	, PROP3GET_INIT(TimeSpan, Milliseconds)
	, PROP3GET_INIT(TimeSpan, Minutes)
	, PROP3GET_INIT(TimeSpan, Seconds)
	, PROP3GET_INIT(TimeSpan, Ticks)
	, PROP3GET_INIT(TimeSpan, TotalDays)
	, PROP3GET_INIT(TimeSpan, TotalHours)
	, PROP3GET_INIT(TimeSpan, TotalMilliseconds)
	, PROP3GET_INIT(TimeSpan, TotalMinutes)
	, PROP3GET_INIT(TimeSpan, TotalSeconds)
{
	CalculateTicks(days, hours, minutes, seconds, milliseconds, true, _ticks);
}
PROP3_GET_CPP(TimeSpan, int, Days)
{
	return (int)(_ticks / TicksPerDay);
}
PROP3_GET_CPP(TimeSpan, int, Hours)
{
	return (int)(_ticks % TicksPerDay / TicksPerHour);
}
PROP3_GET_CPP(TimeSpan, int, Milliseconds)
{
	return (int)(_ticks % TicksPerSecond / TicksPerMillisecond);
}
PROP3_GET_CPP(TimeSpan, int, Minutes)
{
	return (int)(_ticks % TicksPerHour / TicksPerMinute);
}
PROP3_GET_CPP(TimeSpan, int, Seconds)
{
	return (int)(_ticks % TicksPerMinute / TicksPerSecond);
}
PROP3_GET_CPP(TimeSpan, long long, Ticks)
{
	return _ticks;
}
PROP3_GET_CPP(TimeSpan, double, TotalDays)
{
	return (double)_ticks / TicksPerDay;
}
PROP3_GET_CPP(TimeSpan, double, TotalHours)
{
	return (double)_ticks / TicksPerHour;
}
PROP3_GET_CPP(TimeSpan, double, TotalMilliseconds)
{
	return (double)_ticks / TicksPerMillisecond;
}
PROP3_GET_CPP(TimeSpan, double, TotalMinutes)
{
	return (double)_ticks / TicksPerMinute;
}
PROP3_GET_CPP(TimeSpan, double, TotalSeconds)
{
	return (double) _ticks / TicksPerSecond;
}
TimeSpan TimeSpan::Add(TimeSpan ts)
{
	try
	{
		return TimeSpan(_ticks + ts.Ticks);
    }
    catch (const OverflowException&)
	{
		throw OverflowException("Resulting timespan is too big.");
    }
}
int TimeSpan::Compare(TimeSpan t1, TimeSpan t2)
{
	if (t1._ticks < t2._ticks)
	{
		return -1;
	}
    if (t1._ticks > t2._ticks)
	{
		return 1;
	}
    return 0;
}
int TimeSpan::CompareTo(object* value)
{
	if (value == null)
		return 1;

    if (!(instanceof(value, TimeSpan)))
	{
		throw ArgumentException("Argument has to be a TimeSpan.", "value");
    }

    return Compare(*this, *(TimeSpan*)value);
}
int TimeSpan::CompareTo(TimeSpan* value)
{
	return Compare(*this, *value);
}
TimeSpan TimeSpan::Duration()
{
	try
	{
		return TimeSpan(Math::Abs((long long)_ticks));
    }
    catch (OverflowException)
	{
		throw OverflowException("This TimeSpan value is MinValue so you cannot get the duration.");
    }
}
bool TimeSpan::Equals(object* value)
{
	if (!(instanceof(value, TimeSpan)))
		return false;

    return _ticks == ((TimeSpan*)value)->_ticks;
}
bool TimeSpan::Equals(TimeSpan* obj)
{
	return obj->_ticks == _ticks;
}
bool TimeSpan::Equals(TimeSpan t1, TimeSpan t2)
{
	return t1._ticks == t2._ticks;
}
TimeSpan TimeSpan::FromDays(double value)
{
	return From(value, TicksPerDay);
}
TimeSpan TimeSpan::FromHours(double value)
{
	return From(value, TicksPerHour);
}
TimeSpan TimeSpan::FromMilliseconds(double value)
{
	return From(value, TicksPerMillisecond);
}
TimeSpan TimeSpan::FromMinutes(double value)
{
	return From(value, TicksPerMinute);
}
TimeSpan TimeSpan::FromSeconds(double value)
{
	return From(value, TicksPerSecond);
}
TimeSpan TimeSpan::FromTicks(long value)
{
	return TimeSpan(value);
}
int TimeSpan::GetHashCode()
{
	//return UInt64(_ticks).GetHashCode();
	throw;
}
TimeSpan TimeSpan::Negate()
{
	if(_ticks == MinValue._ticks)
		throw OverflowException("This TimeSpan value is MinValue and cannot be negated.");

    return TimeSpan(-_ticks);
}
TimeSpan TimeSpan::Parse(string* s)
{
	throw;
}
TimeSpan TimeSpan::Parse(string* input, IFormatProvider* formatProvider)
{
	throw;
}
TimeSpan TimeSpan::ParseExact(string* input, string* format, IFormatProvider* formatProvider)
{
	throw;
}
TimeSpan TimeSpan::ParseExact(string* input, Array<string>* formats, IFormatProvider* formatProvider)
{
	throw;
}
TimeSpan TimeSpan::ParseExact(string* input, string* format, IFormatProvider* formatProvider, Globalization::TimeSpanStyles* styles)
{
	throw;
}
TimeSpan TimeSpan::ParseExact(string* input, Array<string>* formats, IFormatProvider* formatProvider, Globalization::TimeSpanStyles* styles)
{
	throw;
}
TimeSpan TimeSpan::Subtract(TimeSpan ts)
{
	try
	{
		return TimeSpan(_ticks - ts.Ticks);
    }
    catch (OverflowException)
	{
		throw OverflowException("Resulting timespan is too big.");
    }
}
GC_PTR<string> TimeSpan::ToString() const
{
	Text::StringBuilder sb(14);
                        
    if (_ticks < 0)
		sb.Append('-');

    if (Days != 0)
	{
        sb.Append(Math::Abs(Days));
        sb.Append('.');
    }

    sb.Append(Int32(Math::Abs(Hours)).ToString("D2"));
    sb.Append(':');
    sb.Append(Int32(Math::Abs(Minutes)).ToString("D2"));
    sb.Append(':');
    sb.Append(Int32(Math::Abs(Seconds)).ToString("D2"));


    int fractional = (int)Math::Abs((double)(_ticks % TicksPerSecond));
    if (fractional != 0)
	{
        sb.Append('.');
        sb.Append(Int32(fractional).ToString("D7"));
    }

    return sb.ToString ();
}
GC_PTR<string> TimeSpan::ToString(string* format)
{
	return ToString(format, null);
}
GC_PTR<string> TimeSpan::ToString(string* format, IFormatProvider* formatProvider)
{
	throw;
}
bool TimeSpan::TryParse(string* s, TimeSpan out result)
{
	throw;
}
bool TimeSpan::TryParse(string* input, IFormatProvider* formatProvider, TimeSpan out result)
{
	throw;
}
bool TimeSpan::TryParseExact(string* input, string* format, IFormatProvider* formatProvider, TimeSpan out result)
{
	throw;
}
bool TimeSpan::TryParseExact(string* input, Array<string>* formats, IFormatProvider* formatProvider, TimeSpan out result)
{
	throw;
}
bool TimeSpan::TryParseExact(string* input, string* format, IFormatProvider* formatProvider, Globalization::TimeSpanStyles* styles, TimeSpan out result)
{
	throw;
}
bool TimeSpan::TryParseExact(string* input, Array<string>* formats, IFormatProvider* formatProvider, Globalization::TimeSpanStyles* styles, TimeSpan out result)
{
	throw;
}